<script setup>
// 引入 vue-cropper 组件的样式
import 'vue-cropper/dist/index.css';
import {VueCropper} from "vue-cropper";
import {nextTick, onMounted, reactive, ref, watch} from "vue";
import {ElMessage} from "element-plus";
import {Refresh, RefreshLeft, RefreshRight, UploadFilled, ZoomIn, ZoomOut} from "@element-plus/icons-vue";

const emits = defineEmits(['getBase64', 'getFormData']);
const props = defineProps({
  // 图片地址：url、base64、blob 都可以
  url: {
    type: String,
    default: ''
  },
  // 组件显示的图片大小
  imageSize: {
    type: Number,
    default: 100
  },
  toolbar: {
    type: Boolean,
    default: false
  },
  options: {
    type: Object,
    default: () => {
      return {
        // 裁剪图片的地址：url、base64、blob 都可以
        img: '',
        // 图片背景填充色
        fillColor: '#000',
        // 是否默认生成截图框
        autoCrop: true,
        // 默认生成截图框宽度
        autoCropWidth: 200,
        // 默认生成截图框高度
        autoCropHeight: 200,
        // 是否固定截图框大小
        fixedBox: true
      };
    }
  }
});
// 组件挂载，触发图片裁剪的初始化方法
onMounted(() => {
  initImage(props.url);
});
const imageUrl = ref(props.url);
const fileList = ref([]);
const dialog = reactive({
  visible: false,
  title: '图片裁剪',
  width: '50%',
});
// 监听图片地址的变化
watch(() => props.url, (newUrl) => {
  initImage(newUrl);
}, {deep: true});
// 监听图片地址的变化
watch(() => imageUrl, (newUrl) => {
  initImage(newUrl);
});
// 初始化图片
const initImage = (newUrl) => {
  imageUrl.value = newUrl;
  fileList.value = [];
  fileList.value.push({url: newUrl});
  cropper.options.fillColor = props.options['fillColor'];
  console.log('裁剪图片地址:', newUrl)
};
// 图片裁剪
const cropperRef = ref(null);
const cropper = reactive({
  previews: {},
  previewStyle: {},
  options: props.options
});
// 处理裁剪图片的实时预览方法
const handleRealTimePreview = (data) => {
  cropper.previews = data;
};
// 自定义边框样式
const customStyle = ref({
  borderRadiusStyle: {
    borderRadius: '0'
  },
  rowHeight: {
    height: '300px'
  },
  button: {
    borderRadius: '0',
    padding: '4px 6px'
  }
});
// 确定上传图片
const confirmUpload = async () => {
  if (!cropper.previews['url']) {
    ElMessage.warning('请选择图片');
    return;
  }
  await cropperRef['value'].getCropData(async (base64) => {
    // 向父组件触发一个获取 base64 的事件
    emits('getBase64', base64);
  });
  await cropperRef['value'].getCropBlob(async (blobData) => {
    const formData = new FormData();
    // 这里将图片数据放到formData里面,并且指定一个文件名称,方便后端获取到文件后缀名称
    formData.append('file', blobData, 'image-cropper.jpeg');
    // 向父组件触发一个 upload 的事件
    emits('getFormData', formData);
  });
};
// 关闭事件
const close = () => {
  dialog.visible = false;
};
// 打开图片裁剪弹出框
const openCropperDialog = async () => {
  dialog.visible = true;
  // 重置默认背景颜色
  handleChangeColor();
  // 等待组件渲染完成之后，再执行裁剪图片的初始化方法
  await nextTick();
  // 设置裁剪图片的地址
  cropper.options.img = imageUrl.value;
};

// 上传图片之前预处理,获取到图片,并且设置到图片裁剪器里面
const beforeUploadImage = (file) => {
  // 如果不是图片类型，则提示用户
  if (file.type.indexOf('image/') === -1) {
    ElMessage.error('请上传图片类型文件');
  } else {
    // 创建文件读取器
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => {
      cropper.options.img = reader.result;
    };
  }
};
// 图片裁剪工具栏
const toolbar = (action, num) => {
  if (action === 'zoom') {
    cropperRef['value'].changeScale(num);
  } else if (action === 'rotate') {
    num > 0 ? cropperRef['value'].rotateRight() : cropperRef['value'].rotateLeft();
  } else if (action === 'refresh') {
    cropperRef['value'].refresh();
    handleChangeColor();
  }
};
const handleChangeColor = (val) => {
  cropper.options.fillColor = val || '#000';
};
// 对外暴露方法
defineExpose({
  close
});
</script>

<template>
  <span id="cropper-box">
    <!--  图片回显预览  -->
    <el-avatar @click="openCropperDialog" :size="imageSize" :src="imageUrl"/>
    <!--  图片裁剪弹出框  -->
    <el-dialog v-model="dialog.visible" :title="dialog.title" :width="dialog.width" :close-on-click-modal="false"
               :append-to-body="true" :style="customStyle.borderRadiusStyle" :modal="false" draggable
               @close="close">
      <!--  图片裁剪区域  -->
      <el-row :gutter="20">
        <el-col :span="14" :style="customStyle.rowHeight">
          <!--   这里使用 vue-cropper 图片裁剪组件       -->
          <VueCropper
              ref="cropperRef"
              :img="cropper.options['img']"
              :info="true"
              :fillColor="cropper.options.fillColor"
              :auto-crop="cropper.options.autoCrop"
              :auto-crop-width="cropper.options.autoCropWidth"
              :auto-crop-height="cropper.options.autoCropHeight"
              :fixed-box="cropper.options.fixedBox"
              @realTime="handleRealTimePreview"
          />
        </el-col>
        <el-col :span="10" :style="customStyle.rowHeight">
          <!-- 图片裁剪之后的预览区域 -->
          <div class="image-cropper-preview-box">
            <div class="image-cropper-preview" :style="{background: cropper.options.fillColor}">
              <img alt="" :src="cropper.previews.url" :style="cropper.previews.img"/>
            </div>
          </div>
        </el-col>
      </el-row>
      <!--  操作按钮  -->
      <template #footer>
        <div style="display: flex;">
          <div style="display:flex;flex:2;justify-content: flex-start;">
            <el-upload style="display: inline-block;margin-right: 12px;" action="#" :show-file-list="false"
                       :http-request="()=>{}"
                       :before-upload="beforeUploadImage">
              <el-button size="small" :style="customStyle.borderRadiusStyle">
                <el-icon color="#A8ABB2" size="16">
                  <UploadFilled/>
                </el-icon>
              </el-button>
            </el-upload>
              <el-button @click="toolbar('refresh', 0)" :style="customStyle.button" size="small">
                <el-icon><Refresh/></el-icon>
              </el-button>
              <el-button @click="toolbar('zoom', 1)" :style="customStyle.button" size="small">
                <el-icon><ZoomIn/></el-icon>
              </el-button>
              <el-button @click="toolbar('zoom', -1)" :style="customStyle.button" size="small">
                <el-icon><ZoomOut/></el-icon>
              </el-button>
              <el-button @click="toolbar('rotate', 1)" :style="customStyle.button" size="small">
                <el-icon><RefreshRight/></el-icon>
              </el-button>
              <el-button @click="toolbar('rotate', -1)" :style="customStyle.button" size="small">
                <el-icon><RefreshLeft/></el-icon>
              </el-button>
              <el-color-picker v-model="cropper.options.fillColor" @change="handleChangeColor"/>
          </div>
          <div style="display:flex;flex: 1;justify-content: flex-end;">
            <el-button @click="confirmUpload" color="#1D93AB" :style="customStyle.borderRadiusStyle" size="small">确定
            </el-button>
            <el-button @click="close" type="warning" :style="customStyle.borderRadiusStyle" size="small">取消
            </el-button>
          </div>
        </div>
      </template>
    </el-dialog>
  </span>
</template>

<style scoped lang="scss">
:deep(.el-color-picker__trigger) {
  border-radius: 0;
  height: 24px;
  width: 26px;
  margin-left: 12px;
}

:deep(.el-color-picker__color) {
  border-radius: 0;
}

#cropper-box {
  display: inline-block;
  
  > .el-avatar {
    position: relative;
  }
  
  > .el-avatar:hover::after {
    content: '+';
    display: flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    border-radius: 50%;
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    color: #eee;
    background: rgba(0, 0, 0, .5);
    font-size: 36px;
    font-style: normal;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
}

.image-cropper-preview-box {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  
  .image-cropper-preview {
    width: 200px;
    height: 200px;
    border-radius: 50%;
    box-shadow: 0 0 4px #ccc;
    overflow: hidden;
  }
}
</style>